package info.tsukaworld.actor.mob.pathfinding;


import info.tsukaworld.grid.*;


import java.util.HashSet;
import java.util.PriorityQueue;
import java.util.Set;


public class AStar
{
    private final SimpleHeuristicCalculator calculator;

    public AStar(final SimpleHeuristicCalculator calc)
    {
        calculator = calc;
    }

    public final Path findPath( Maze     maze,
                                Location startLoc,
                                Location endLoc)
    {
        final PriorityQueue<Path> paths;
        final Set<Tile>           openList;
        final Set<Tile>           closedList;
        final Tile                startTile;
        final Tile                endTile;
        Path                      shortestPath;

        paths        = new PriorityQueue<Path>();
        openList     = new HashSet<Tile>();
        closedList   = new HashSet<Tile>();
        startTile    = maze.getTileAt(startLoc);
        endTile      = maze.getTileAt(endLoc);
        addToOpenList(null, startTile, endTile, paths, openList);
        shortestPath = null;
        
        while(openList.size() != 0)
        {
            final Path      currentPath;
            final Set<Tile> adjacentTiles;
            final Tile      currentTile;

            currentPath = paths.remove();
            currentTile = currentPath.getTile();

            if(currentTile.equals(endTile))
            {
                shortestPath = currentPath;
                break;
            }

            openList.remove(currentTile);
            closedList.add(currentTile);            
            adjacentTiles = maze.getTilesAround(currentPath.getTile().getLocation());

            for(final Tile tile : adjacentTiles)
            {
                if(!(openList.contains(tile)))
                {
                    if(!(closedList.contains(tile)))
                    {
                        addToOpenList(currentPath, tile, endTile, paths, openList);
                    }
                }
            }
        }

        return (shortestPath);
    }

    private void addToOpenList(final Path                parentPath,
                               final Tile                currentTile,
                               final Tile                endTile,
                               final PriorityQueue<Path> paths,
                               final Set<Tile>           openList)
    {
        final int  heuristic;
        final Path path;
        
        heuristic = calculator.calculateHeuristic(currentTile, endTile);
        path      = new Path(parentPath, currentTile, heuristic);
        paths.add(path);
        openList.add(currentTile);
    }
}